#include "StdAfx.h"
#include "MapfileParser.h"

/******************************************************************************
 * CMapfileParser Implementation
 *****************************************************************************/

CMapfileParser::CMapfileParser(void)
{
}

CMapfileParser::~CMapfileParser(void)
{
}

CString CMapfileParser::Parse(const CString& strMapfile, CNodePtrVec& vecNodes, 
                              CPathPtrVec& vecPaths)
{
    // Open file if exists
    CStdioFile file;
    if (!file.Open(strMapfile, CFile::modeRead))
    {
        CString strMsg;
        strMsg.Format("Unable to find map file: %s", strMapfile);
        AfxMessageBox(strMsg);
    } 

    // Parse out
    CString strDesc = ParseDesc(file);

    // First pass, create a list of unlinked nodes
    FirstPass(file, vecNodes);

    // Reset file
    file.SeekToBegin();

    // Second pass, link up nodes into mesh
    SecondPass(file, vecNodes);

    // Reset file again
    file.SeekToBegin();

    // Now parse out paths
    ParsePaths(file, vecNodes, vecPaths);

    // Return the first node as the arbitrary root
    return strDesc;
}

CString CMapfileParser::ParseDesc(CStdioFile& file)
{
    CString strLine;
    while (file.ReadString(strLine))
    {
        // Ignore line if its empty or a comment
        strLine.Trim();
        if (strLine.IsEmpty() || strLine[0] == '#')
            continue;  

        // Tokenizer info
        int iTok = 0;
        CString strTok;

        strTok = strLine.Tokenize("[", iTok);
        if (strTok == "desc")
        {
            return strLine.Tokenize("]", iTok);
        }
    }

    return "";
}

bool CMapfileParser::FirstPass(CStdioFile& file, CNodePtrVec& vecNodes)
{
    CString strLine;
    while (file.ReadString(strLine))
    {
        // Ignore line if its empty or a comment
        strLine.Trim();
        if (strLine.IsEmpty() || strLine[0] == '#')
            continue;

        CString strTok; // Token
        int iTok = 0;   // Token position
        
        CString strType = strLine.Tokenize("[", iTok);
        if (strType == "node")
        {
            vecNodes.push_back(ParseNodeFP(strLine, iTok));
        }
    }

    return true;
}

bool CMapfileParser::SecondPass(CStdioFile& file, CNodePtrVec& vecNodes)
{
    if (vecNodes.empty())
        return false;

    CString strLine;
    CNodePtrVecIter it = vecNodes.begin()++;
    while (file.ReadString(strLine))
    {
        // Ignore line if its a comment or blank
        strLine.Trim();
        if (strLine.IsEmpty() || strLine[0] == '#')
            continue;

        CString strTok; // Token
        int iTok = 0;   // Token position

        // Check type
        CString strType = strLine.Tokenize("[", iTok);
        if (strType == "node")
        {       
            ParseNodeSP(strLine, iTok, *it, vecNodes);
            it++;
        }
    }  
    
    return true;
}


CNode* CMapfileParser::GetNodePointer(int id, CNodePtrVec vecNodes)
{
    if (id < 0)
        return NULL;

    for (CNodePtrVecIter it = vecNodes.begin(); it != vecNodes.end(); ++it)
    {
        if ((*it)->m_id == id)
            return *it;
    }

    return NULL;
}

CNode* CMapfileParser::ParseNodeFP(CString& strLine, int& iTok)
{
    // Prepare new node
    CNode* pNode = new CNode();

    // Parse out ID, X, Y, and inlet/outlet
    pNode->m_id = atoi(strLine.Tokenize("]", iTok));
    iTok++; // Incriment once here to pass '('
    pNode->m_pt.x = atoi(strLine.Tokenize(",", iTok));
    pNode->m_pt.y = atoi(strLine.Tokenize(",", iTok));
    if (atoi(strLine.Tokenize(",", iTok)))
        pNode->m_type |= NF_INLET;
    if (atoi(strLine.Tokenize(")", iTok)))            
        pNode->m_type |= NF_OUTLET;

    // Insert into vector
    return pNode;
}

void CMapfileParser::ParseNodeSP(CString& strLine, int& iTok, CNode* n, 
                                 CNodePtrVec& vecNodes)
{     
    // Get up until colon
    CString strTok;
    strTok = strLine.Tokenize(":", iTok);        

    // Parse out and find cardinal directions
    strTok = strLine.Tokenize(",", iTok).Trim();
    n->m_north = GetNodePointer(atoi(strTok), vecNodes);   
    strTok = strLine.Tokenize(",", iTok).Trim();
    n->m_east = GetNodePointer(atoi(strTok), vecNodes); 
    strTok = strLine.Tokenize(",", iTok).Trim();
    n->m_south = GetNodePointer(atoi(strTok), vecNodes);  
    strTok = strLine.Tokenize("\n", iTok).Trim();
    n->m_west = GetNodePointer(atoi(strTok), vecNodes); 
}

void CMapfileParser::ParsePaths(CStdioFile& file, const CNodePtrVec& vecNodes,
                                CPathPtrVec& vecPaths)
{
    CString strLine;
    while(file.ReadString(strLine))
    {
        // Check type
        int iTok = 0;
        CString strType = strLine.Tokenize("[", iTok);
        if (strType != "path")
        {
            continue;    
        }

        // Parse out source and dest indecies
        CString temp = strLine.Tokenize("(", iTok);
        int src = atoi(strLine.Tokenize(",", iTok));
        int dest = atoi(strLine.Tokenize(")", iTok));
        iTok++;

        // Initialize path
        CPath* path = new CPath(vecNodes[src], vecNodes[dest]);

        while (iTok > 0)
        {
            CString temp = strLine.Tokenize(",\n", iTok);
            if (!temp.IsEmpty())   
                path->Add(vecNodes[atoi(temp)]);
        }

        vecPaths.push_back(path);
    }
}